package cn.suimg.wechat.util;

import cn.suimg.common.Constant;
import cn.suimg.common.EncryptUtil;
import cn.suimg.common.HttpClientUtil;
import cn.suimg.wechat.bean.CorpWechat;
import cn.suimg.wechat.enmus.MediaFileType;
import cn.suimg.wechat.enmus.PublishMessageType;
import cn.suimg.wechat.redis.CacheKeys;
import cn.suimg.wechat.redis.CacheOperator;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;

/**
 * @author Suimg
 */
@Data
public class CorpWechatUtil {

    /**
     * 实例ID由三个字段md5生成
     */
    private String instanceId;

    /**
     * 企业微信ID
     */
    private String corpId;

    /**
     * 企业微信密钥
     */
    private String corpSecret;

    /**
     * 推送应用ID
     */
    private String agentId;

    /**
     * 缓存管理器
     */
    private CacheOperator cacheOperator;

    /**
     * 实例缓存
     */
    private static final Map<String, CorpWechatUtil> INSTANCES_CACHE = new HashMap<>();

    /**
     * Logger
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(CorpWechatUtil.class);

    /**
     * 简单的获取实例方法
     * @param corpId
     * @param corpSecret
     * @param agentId
     * @return
     */
    public static CorpWechatUtil getInstance(String corpId, String corpSecret, String agentId){
        String instanceId = EncryptUtil.md5(corpId + corpSecret + agentId);
        CorpWechatUtil instance;
        if((instance = INSTANCES_CACHE.get(instanceId)) == null){
            instance = new CorpWechatUtil(corpId, corpSecret, agentId);
            INSTANCES_CACHE.put(instanceId,instance);
        }
        return instance;
    }

    /**
     * 私有构造方法
     * @param corpId
     * @param corpSecret
     * @param agentId
     */
    private CorpWechatUtil(String corpId, String corpSecret, String agentId){
        this.corpId = corpId;
        this.corpSecret = corpSecret;
        this.agentId = agentId;
        this.cacheOperator = SpringContextUtils.getBean(CacheOperator.class);
        this.instanceId = EncryptUtil.md5(corpId + corpSecret + agentId);
    }

    /**
     * 推送消息
     * @param receiveInfo
     * @param publishMessageType
     * @param content
     */
    public boolean publishMessage(String receiveInfo,PublishMessageType publishMessageType,Object content){
        if(publishMessageType == PublishMessageType.TextMessage)
            return publishMessage(receiveInfo,PublishMessageType.TextMessage,() ->
                    new HashMap<String, Object>(1){{
                        put("text",new HashMap<String, Object>(1){{
                            put("content", content);
                        }});
                    }}
            );
        else if(publishMessageType == PublishMessageType.MarkdownMessage)
           return publishMessage(receiveInfo,PublishMessageType.MarkdownMessage,() ->
                    new HashMap<String, Object>(1){{
                        put("markdown",new HashMap<String, Object>(1){{
                            put("content", EncryptUtil.base64Decode((String) content));
                        }});
                    }}
            );
        else if(publishMessageType == PublishMessageType.FileMessage)
            return publishMessage(receiveInfo,PublishMessageType.FileMessage,() ->
                    new HashMap<String, Object>(1){{
                        put("file",new HashMap<String, Object>(1){{
                            put("media_id",uploadMedia((File) content,MediaFileType.File));
                        }});
                    }}
            );
        else if(publishMessageType == PublishMessageType.ImageMessage)
            return publishMessage(receiveInfo,PublishMessageType.ImageMessage,() ->
                    new HashMap<String, Object>(1){{
                        put("image",new HashMap<String, Object>(1){{
                            put("media_id",uploadMedia((File) content,MediaFileType.Image));
                        }});
                    }}
            );
        else if(publishMessageType == PublishMessageType.VoiceMessage)
            return publishMessage(receiveInfo,PublishMessageType.VoiceMessage,() ->
                    new HashMap<String, Object>(1){{
                        put("image",new HashMap<String, Object>(1){{
                            put("voice",uploadMedia((File) content,MediaFileType.Voice));
                        }});
                    }}
            );

        else if(publishMessageType == PublishMessageType.VideoMessage)
            return publishMessage(receiveInfo,PublishMessageType.VoiceMessage,() ->
                    new HashMap<String, Object>(1){{
                        put("video",new HashMap<String, Object>(3){{
                            put("media_id",uploadMedia((File) content,MediaFileType.Video));
                            put("title","Input your title here");
                            put("description","description...");
                        }});
                    }}
            );
        return false;
    }

    /**
     * 推送消息
     * @param receiveInfo
     * @param publishMessageType
     * @param contentSupplier
     */
    public boolean publishMessage(String receiveInfo, PublishMessageType publishMessageType, Supplier<Map<String, Object>> contentSupplier){
        @SuppressWarnings("unchecked")
        Map<String, Object> receiveData = (Map<String, Object>) JSONObject.parseObject(receiveInfo).toJavaObject(Map.class);
        String response = HttpClientUtil.postJson(String.format(Constant.CORP_WECHAT_SEND_MESSAGE_URL, getAccessToken()), new HashMap<String, Object>() {{
            putAll(receiveData);
            put("msgtype", publishMessageType.toString());
            putAll(contentSupplier.get());
            put("agentid", agentId);
            put("enable_duplicate_check", 0);
            put("duplicate_check_interval", 1800);
        }});
        return JSONObject.parseObject(response).getInteger("errcode") == 0;
    }

    /**
     * 获取访问密钥
     * @return
     */
    public String getAccessToken(){
        CacheKeys cacheKey = CacheKeys.keyLink(CacheKeys.CORP_WECHAT_ACCESS_TOKEN, instanceId);
        String accessToken;
        if((accessToken = cacheOperator.get(cacheKey)) == null){
            String response = HttpClientUtil.getData(Constant.CORP_WECHAT_GET_ACCESS_TOKEN_URL, corpId, corpSecret);
            JSONObject jsonObject = JSONObject.parseObject(response);
            if(jsonObject.getInteger("errcode") == 0){
                accessToken = jsonObject.getString("access_token");
                int expiresIn = jsonObject.getInteger("expires_in");
                cacheOperator.set(cacheKey,accessToken,expiresIn);
            }else {
                accessToken = null;
                LOGGER.warn("getAccessToken error:{}",jsonObject.getString("errmsg"));
            }
        }
        return accessToken;
    }


    /**
     * 查询企业名称
     * @return
     */
    public String getCorpName(){
        String response = HttpClientUtil.getData(Constant.CORP_WECHAT_GET_DEPT_INFO_URL, getAccessToken());
        Optional<String> corpInfo = JSONObject.parseObject(response).getJSONArray("department").stream()
                .map(object0 -> (JSONObject) object0)
                .filter(jsonObject -> jsonObject.getInteger("parentid") == 0)
                .map(jsonObject -> jsonObject.getString("name"))
                .findFirst();
        if(!corpInfo.isPresent()){
            LOGGER.warn("get corp info failed");
            return "未知企业信息";
        }
        return corpInfo.get();
    }

    /**
     * 获取应用信息
     * @return
     */
    public CorpWechat getAgentInfo(){
        String response = HttpClientUtil.getData(Constant.CORP_WECHAT_GET_AGENT_INFO_URL, getAccessToken(), agentId);
        JSONObject jsonObject = JSONObject.parseObject(response);
        return new CorpWechat(){{
            setAgentId(agentId);
            setAgentName(jsonObject.getString("name"));
            setAgentAvatarUrl(jsonObject.getString("square_logo_url"));
        }};
    }

    /**
     * 上传媒体文件
     * @param file
     * @param mediaFileType
     * @return
     */
    public String uploadMedia(File file, MediaFileType mediaFileType){
        try {
            String response = new HttpClientUtil()
                    .post(Constant.CORP_WECHAT_UPLOAD_MEDIA_URL, getAccessToken(), mediaFileType)
                    .postFile(file)
                    .exec()
                    .getResponseData();
            return JSONObject.parseObject(response).getString("media_id");
        }catch (FileNotFoundException ignored){}
        return null;
    }

}
